#pragma once
#ifndef FILEREADER_HPP_
#define FILEREADER_HPP_

#include "Types.hpp"
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <fstream>
#include <stdlib.h>
#include <stdio.h>

enum Param_Type {
    Param_type_int,
    Param_type_double,
    Param_type_string,
    Param_type_UnDef
};

inline std::string ParamTypeToString(Param_Type x)
{
    if (x == Param_type_int) {
        return "INT";
    }
    else if (x == Param_type_double) {
        return "DOUBLE";
    }
    else if (x == Param_type_string){
        return "STRING";
    }
    return "UNDEFINED";
}

inline Param_Type StringToParamType(std::string x)
{
    if ((x == "INT") || (x == "int")) {
        return Param_type_int;
    }
    else if ((x == "DOUBLE") || (x == "double")) {
        return Param_type_double;
    }
    else if ((x == "STRING") || (x == "string")){
        return Param_type_string;   
    }
    else {
        return Param_type_UnDef;
    }
}

struct key_struct
{
    std::string parameter_name;
    Param_Type ptype;
};

class FileReader
{

    public:

    FileReader()
    {
    }
    
    //register a new parameter with name key and initial int value
    void RegisterIntParameter(const std::string& key, int init);

    //register a new parameter with name key and initial double value
    void RegisterDoubleParameter(const std::string& key, double init);

    //register a new parameter with name key and initial string value
    void RegisterStringParameter(const std::string& key, const std::string &init);
    
    //print key, typeparam tuple to std::out
    void PrintKeyTuples(std::ostream& out) const;
    
    //print out all parameters to std:out
    void PrintParameters(std::ostream& out) const;

    //set a value for the key string with value in
    void SetParameter(const std::string &key, const std::string &in);

    //set a value for the key string with value in
    void SetParameter(const std::string &key, double in);

    //set a value for the key string with value in
    void SetParameter(const std::string &key, int in);

    // get the int value of key 
    cl_int GetIntParameter(const std::string &key) const;

    // get the double value of key 
    cl_real GetDoubleParameter(const std::string &key) const;
    
    // get the string value of key 
    std::string GetStringParameter(const std::string &key) const;
    
    // returns a constant reference of the vector of names
    inline const std::vector<std::string>& GetParameterNames()
    {return parameter_names_;}
    // returns a constant reference of the vector of key_struct
    inline const std::vector<key_struct>& Getkeys()
    {return keys_;}


    //try to read all registered parameters from filename
    bool ReadFile(const std::string &name);
    /*
     *Read the particle file
     *cl_float* masses: pointer to be allocated in this function that contains the mass of particles
     *cl_float3* positions: pointer to be allocated in this function that contains the x, y z position
     *cl_float3* velocities: pointer to be allocated in this function that contains the x,y,z velocity
     *IMPORTANT: In case there is an error it will free the memory and return false;
    */
    bool readParticleFile(cl_uint& N,cl_real*& masses,cl_real4*& positions,cl_real4*& velocities, const std::string& filename);
    //Search for the type of a key
    Param_Type GetParamType(const std::string& key) const;

    private:
        //vector with all the keys{parameter,type}
        std::vector<key_struct> keys_;
        //vector with all the parameter_names
        std::vector<std::string> parameter_names_;
        //maps to the corresponding parametervalue
        std::map<std::string,int> int_params_;
        std::map<std::string,double> double_params_;
        std::map<std::string,std::string> string_params_;

};

#endif //FILEREADER_HPP_
